/*
 * Guild.cpp
 *
 *  Created on: 2014. 7. 17.
 *      Author: mega
 */

#include "../Guild.h"
#include "../Ack.h"
#include "../Enum.h"
#include "../Static.h"
#include "../NResource/Static.h"
#include "../GuildRank.h"

#include <Dispatcher/Session.h>


namespace GuildServer {

Guild::Guild() : m_GuildSeq(0), m_ChannelKey(0), m_bFlushDB(false), m_bWorkTimer(false), m_tmRankUpdated(0)
{
	m_pTimer = new ::Dispatcher::Timer();

	m_GuildInfo.Clear();
	m_MemberTree.clear();
}

Guild::~Guild()
{

}

void
Guild::Initialize(uint32_t channelKey, uint32_t guildSeq)
{
	m_ChannelKey = channelKey;
	m_GuildSeq = guildSeq;
	m_bFlushDB = false;
	m_GuildInfo.Clear();
	m_MemberTree.clear();

	m_tmRankUpdated = 0;
}

void
Guild::Finalize()
{

}

// DB에서 data를 load
int
Guild::LoadData( uint32_t guild_seq )
{
	m_tmRankUpdated = 0;

	// 길드 정보 loading
	{
		Database::ResultSet res = Database::Execute(static_cast<int>(DatabaseType::GUILD_MASTER), "call sp_select_guild_info(", guild_seq, ")");

		if (0 != res.GetSQLError())
		{
			LOG(ERR, __FUNCTION__, " : Fail call sp_select_guild_info");
			return ACK_DB_ERROR;
		}

		if (0 == res.GetRowCount())
		{
			LOG(ERR, __FUNCTION__, " : not exists guild");
			return ACK_DB_ERROR;
		}

		for (Database::ResultSet::iterator iter = res.begin(); iter != res.end(); iter++)
		{
			m_GuildSeq = iter->getInt("guild_seq"); // result
			m_GuildInfo._name = iter->getString("guild_name").c_str();
			m_GuildInfo._masterSeq = iter->getInt("guild_captain_seq");
			m_GuildInfo._grade = iter->getInt("guild_grade");
			m_GuildInfo._exposure = iter->getInt("guild_is_exposure");
			m_GuildInfo._intro = iter->getString("guild_intro").c_str();
			m_GuildInfo._buffLevel = iter->getInt("guild_buff_level");
			m_GuildInfo._buffType = iter->getInt("guild_buff_type");
			m_GuildInfo._buffValue = iter->getInt("guild_buff_value");
			m_GuildInfo._score = iter->getInt("guild_score");
			m_GuildInfo._password = iter->getString("guild_password").c_str();
		}
	}

	// 길드 멤버 loading
	{
		Database::ResultSet res = Database::Execute(static_cast<int>(DatabaseType::GUILD_MASTER), "call sp_select_guild_member_info(", guild_seq, ")");

		if (0 != res.GetSQLError())
		{
			LOG(ERR, __FUNCTION__, " : Fail call sp_select_guild_member_info");
			return ACK_DB_ERROR;
		}

		if (0 == res.GetRowCount())
		{
			LOG(ERR, __FUNCTION__, " : not exists guild member");
			return ACK_DB_ERROR;
		}

		m_MemberTree.clear();

		for (Database::ResultSet::iterator iter = res.begin(); iter != res.end(); iter++)
		{
			Member::MemberInfo clsMemberInfo;
			clsMemberInfo._guildSeq = guild_seq;
			uint32_t user_seq = iter->getInt("guild_user_seq");
			clsMemberInfo._socialID = atoll(iter->getString("guild_socialid").c_str());
			clsMemberInfo._nickname	= iter->getString("guild_member_nickname").c_str();
			clsMemberInfo._profileURL = iter->getString("guild_member_profile_url").c_str();
			clsMemberInfo._level	= iter->getInt("guild_member_level");
			clsMemberInfo._contributeScore = iter->getInt("guild_contribution_score");
			clsMemberInfo._dailyScore = iter->getInt("guild_member_daily_score");
			clsMemberInfo._dailyInitTime = iter->getInt("guild_score_dailytime");
			clsMemberInfo._loginTime	= iter->getInt("guild_login_time");

			// guild_member pool이 static에 포함되어 있다. 그래서 guild와 static 사이에 쌍방 access가 되고 있는 것을 유의하자.
			std::shared_ptr<Member> pMember = Static::Instance().NewMember();
			pMember->Initialize(user_seq);
			pMember->Open(clsMemberInfo);

			AddMember(pMember);
		}
	}

	return ACK_OK;
}

void
Guild::FlushGuildPoint()
{
	if (false == m_bFlushDB)
		return;

	Database::ResultSet res = Database::Execute(static_cast<int>(DatabaseType::GUILD_MASTER),
				"call sp_update_guild_point(",
					m_GuildSeq, ",",
					m_GuildInfo._score, ")"
			);

	if (0 != res.GetSQLError())
	{
		LOG(ERR, __FUNCTION__, " Fail call sp_update_guild_point");
		return;
	}

	auto iter = res.begin();

	int nRet = iter->getInt(1);

	if (0 != nRet)
	{
		LOG(ERR, __FUNCTION__, " Guild point update exception. nRet : ", nRet);
		return;
	}

	m_bFlushDB = false;
}

void
Guild::Open(GuildInfo & info)
{
	m_GuildInfo = info;
}

bool
Guild::AddMember(std::shared_ptr<Member> pMember)
{
	auto iter = m_MemberTree.find(pMember->GetSeq());
	if (iter != m_MemberTree.end())
		return false;

	m_MemberTree[pMember->GetSeq()] = pMember;

	return true;
}

bool
Guild::DelMember(uint32_t userSeq)
{
	auto iter = m_MemberTree.find(userSeq);
	if (iter == m_MemberTree.end())
		return false;

	m_MemberTree.erase(iter);

	return true;
}

std::shared_ptr<Member>
Guild::GetMember(uint32_t userSeq)
{
	auto iter = m_MemberTree.find(userSeq);
	if (iter == m_MemberTree.end())
		return NULL;

	return iter->second;
}

std::shared_ptr<Member>
Guild::GetMaster()
{
	auto iter = m_MemberTree.find(m_GuildInfo._masterSeq);
	if (iter == m_MemberTree.end())
		return NULL;

	return iter->second;
}

bool
Guild::IsExistMember(uint32_t userSeq)
{
	auto iter = m_MemberTree.find(userSeq);
	if (iter != m_MemberTree.end())
		return true;

	return false;
}

bool
Guild::IsGuildMaster(uint32_t userSeq)
{
	if (m_GuildInfo._masterSeq == userSeq)
		return true;

	return false;
}

int
Guild::GetMemberCountExceptMaster()
{
	int nCount = 0;

	auto iter = m_MemberTree.begin();

	for (; iter != m_MemberTree.end(); iter++)
	{
		if (iter->second->GetSeq() == m_GuildInfo._masterSeq)
			continue;

		nCount++;
	}

	return nCount;
}

int
Guild::GetConnectMemberCount()
{
	int nCount = 0;

	auto iter = m_MemberTree.begin();

	for (; iter != m_MemberTree.end(); iter++)
	{
		if (0 >= iter->second->GetServer())
			continue;

		nCount++;
	}

	return nCount;
}

int
Guild::GetMaxMemberCount()
{
	// TODO event driven 방식으로 #member를 산출하게 변경해야 한다.
	NResource::ResourceGuildBanalce * pBalance = NResource::Static::Instance().GetGuildBalance();
	if (NULL == pBalance)
	{
		LOG(ERR, __FUNCTION__, " pBalance is NULL");
		//pChannel->SendFail(ack, ACK_UNKNOWN_ERROR);
		return 0;
	}

	int iMaxCount =  pBalance->GetGuildMaxUser(m_GuildInfo._grade);
	if (0 >= iMaxCount)
	{
		LOG(ERR, __FUNCTION__, " Guild max user get fail. grade : ", m_GuildInfo._grade);
		//pChannel->SendFail(ack, ACK_UNKNOWN_ERROR);
		return 0;
	}

	return iMaxCount;

}

void
Guild::ChangeScore(int score)
{
	NResource::ResourceGuildBanalce * pBalace = NResource::Static::Instance().GetGuildBalance();
	if (NULL == pBalace)
	{
		LOG(ERR, __FUNCTION__, " : pBalance is NULL");
		return;
	}

	int maxScore = pBalace->GetGuildMaxScore();
	if (0 >= maxScore)
	{
		LOG(ERR, __FUNCTION__, " : Guild max score get fail.");
		return;
	}

	m_GuildInfo._score += score;

	if (0 >= m_GuildInfo._score)
		m_GuildInfo._score = 0;

	if (maxScore <= m_GuildInfo._score)
		m_GuildInfo._score = maxScore;

	// TODO : Redis 저장
	::GuildServer::GuildRank& kGuildRank = ::GuildServer::Static::Instance().GetRankList();
	kGuildRank.UpdatePoint( GetSeq(), m_GuildInfo._score );
}

int
Guild::GetRank()
{
	if ( m_tmRankUpdated < (uint64_t)time(NULL) )
	{
		::GuildServer::GuildRank& kGuildRank = ::GuildServer::Static::Instance().GetRankList();
		m_GuildInfo._rank = kGuildRank.GetRank( GetSeq() );
		m_GuildInfo._prevRank = kGuildRank.GetPrevRank( GetSeq() );

		m_tmRankUpdated = (uint64_t)time(NULL);
		m_tmRankUpdated += 60000; // 1분(60초)
	}

	return m_GuildInfo._rank;
}


int16_t
Guild::JoinMember(JoinGuildSyn & syn, uint32_t& a_tmCooldown)
{
	NResource::ResourceGuildBanalce * pBalance = NResource::Static::Instance().GetGuildBalance();
	if (NULL == pBalance)
	{
		LOG(ERR, __FUNCTION__, " pBalance is NULL");
		return ACK_UNKNOWN_ERROR;
	}

	int guildMaxUser =  pBalance->GetGuildMaxUser(m_GuildInfo._grade);
	if (0 >= guildMaxUser)
	{
		LOG(ERR, __FUNCTION__, " Guild max user get fail. grade : ", m_GuildInfo._grade);
		return ACK_UNKNOWN_ERROR;
	}

	// 길드 가입 인원 초과
	if (guildMaxUser <= GetMemberCount())
	{
		LOG(ERR, __FUNCTION__, " Guild member full. guild_seq : ", m_GuildSeq);
		return ACK_GUILD_JOIN_MEMBER_FULL;
	}

	// 길드 가입 점수
	int joinScore = 0;
	{
		auto pFly = pBalance->GetFly(GUILD_JOIN_POINT);
		if (NULL == pFly)
		{
			LOG(ERR, __FUNCTION__, " pFly(GUILD_JOIN_POINT) is NULL");
			return ACK_UNKNOWN_ERROR;
		}

		joinScore = pFly->_VALUE;
	}

	int guildScore = m_GuildInfo._score + joinScore;
	if ( 0 > guildScore )
		guildScore = 0; // added by spencer(2014.08.13), 음수가 나와서 아래에 guildGrade가 -1이 되는 문제가 발생함. 이 경우에는 양수를 더하는 것이라서 이 조건에 걸리지 않을 것이다. 단 최대값 채크를 해야하지 않을까?

	// 길드 최대 점수 제한
	{
		auto pFly = pBalance->GetFly(GUILD_MAX_POINT);
		if (NULL == pFly)
		{
			LOG(ERR, __FUNCTION__, " pFly(GUILD_MAX_POINT) is NULL");
			return ACK_UNKNOWN_ERROR;
		}

		if (guildScore > pFly->_VALUE )
			guildScore = pFly->_VALUE;
	}


	Database::ResultSet res = Database::Execute(static_cast<int>(DatabaseType::GUILD_MASTER),
				"call sp_insert_join_guild(",
					syn.user_seq, "," ,
					m_GuildSeq, "," ,
					"'", syn.socialid, "'," ,
					"'", syn.nickname.c_str(), "'," ,
					"'", syn.profile_url.c_str(), "'," ,
					syn.level, ",",
					joinScore, ",",		// 일일 기여 점수
					joinScore, ",",		// 누적 기여 점수
					guildScore, ")"		// 길드 점수
				);

	if (0 != res.GetSQLError())
	{
		LOG(ERR, __FUNCTION__, " Fail call sp_insert_join_guild. user_seq : ", syn.user_seq, " guild_seq : ", m_GuildSeq);
		return ACK_DB_ERROR;
	}

	auto iter = res.begin();

	int nRet = iter->getInt(1);
	a_tmCooldown = iter->getInt(2);


	if ( ACK_GUILD_JOIN_NOT_COOL_DOWN_YET == nRet )
	{

		LOG(DEV, __FUNCTION__, " not cooldown yet. nRet : ", nRet);
		return nRet;
	}
	else if (0 != nRet)
	{
		LOG(ERR, __FUNCTION__, " Guild member insert exception. nRet : ", nRet);
		return nRet;
	}

	// 길드 가입 점수 반영
	ChangeScore(joinScore);

	// 길드 멤버 생성
	std::shared_ptr<Member> pMember = Static::Instance().NewMember();
	pMember->Initialize(syn.user_seq);

	Member::MemberInfo	clsMemberInfo;
	clsMemberInfo._guildSeq		= syn.guild_seq;
	clsMemberInfo._level		= syn.level;
	clsMemberInfo._nickname		= syn.nickname;
	clsMemberInfo._profileURL	= syn.profile_url;
	clsMemberInfo._regDate		= time(NULL);
	clsMemberInfo._serverid		= syn.server_id;
	clsMemberInfo._socialID		= syn.socialid;
	clsMemberInfo._dailyScore	= joinScore;
	clsMemberInfo._dailyInitTime	= time(NULL);
	clsMemberInfo._loginTime	= time(NULL);
	clsMemberInfo._contributeScore	= joinScore;

	pMember->Open(clsMemberInfo);
	AddMember(pMember);

	return ACK_OK;
}


int16_t
Guild::LeaveMaster()
{
	Database::ResultSet res = Database::Execute(static_cast<int>(DatabaseType::GUILD_MASTER),
				"call sp_update_disband_guild(",
					m_GuildInfo._masterSeq, ",",
					m_GuildSeq, ")"
			);

	if (0 != res.GetSQLError())
	{
		LOG(ERR, __FUNCTION__, " Fail call sp_update_disband_guild. user_seq : ", m_GuildInfo._masterSeq);
		return ACK_DB_ERROR;
	}

	auto iter = res.begin();

	int nRet = iter->getInt(1);

	if (0 != nRet)
	{
		LOG(ERR, __FUNCTION__, " Guild disband exception. nRet : ", nRet);
		return nRet;
	}

	// 길드에서 길드 마스터 삭제
	DelMember(m_GuildInfo._masterSeq);

	// 길드 해체
	Static::Instance().DelGuild(m_GuildSeq);

	::GuildServer::GuildRank& kGuildRank = ::GuildServer::Static::Instance().GetRankList();
	kGuildRank.RemoveGuild( m_GuildSeq );

	return ACK_OK;
}


int16_t
Guild::LeaveMember(uint32_t member_seq)
{
	NResource::ResourceGuildBanalce * pBalance = NResource::Static::Instance().GetGuildBalance();
	if (NULL == pBalance)
	{
		LOG(ERR, __FUNCTION__, " pBalance is NULL");
		return ACK_UNKNOWN_ERROR;
	}

	// 길드 탈퇴 점수
	int leaveScore = 0;
	{
		auto pFly = pBalance->GetFly(GUILD_LEAVE_POINT);
		if (NULL == pFly)
		{
			LOG(ERR, __FUNCTION__, " pFly(GUILD_LEAVE_POINT) is NULL");
			return ACK_UNKNOWN_ERROR;
		}

		leaveScore = pFly->_VALUE;
	}

	int guildScore = m_GuildInfo._score + leaveScore;
	if ( 0 > guildScore )
		guildScore = 0; // added by spencer(2014.08.13), 음수가 나와서 아래에 guildGrade가 -1이 되는 문제가 발생함.
	int guildGrade = pBalance->GetGuildGradeByScore(guildScore);

	if (0 >= guildGrade)
	{
		LOG(ERR, __FUNCTION__, " Guild grade get fail. score : ", guildScore, ", m_GuildInfo._score: ", m_GuildInfo._score);
		return ACK_UNKNOWN_ERROR;
	}

	Database::ResultSet res = Database::Execute(static_cast<int>(DatabaseType::GUILD_MASTER),
				"call sp_update_leave_guild(",
					member_seq, ",",
					m_GuildSeq, ",",
					guildScore, ",",
					guildGrade, ")"
			);

	if (0 != res.GetSQLError())
	{
		LOG(ERR, __FUNCTION__, " Fail call sp_update_leave_guild. user_seq : ", member_seq);
		return ACK_DB_ERROR;
	}

	auto iter = res.begin();

	int nRet = iter->getInt(1);

	if (0 != nRet)
	{
		LOG(ERR, __FUNCTION__, " Guild member leave exception. nRet : ", nRet);
		return nRet;
	}

	ChangeScore(leaveScore);

	// 등급이 낮아 지는 경우 Buff Down
	if (m_GuildInfo._grade > guildGrade)
		UpdateBuffDown();

	m_GuildInfo._grade = guildGrade;

	// 길드에서 멤버 삭제
	DelMember(member_seq);


	return ACK_OK;
}


int16_t
Guild::KickoutMember(uint32_t member_seq)
{
	NResource::ResourceGuildBanalce * pBalance = NResource::Static::Instance().GetGuildBalance();
	if (NULL == pBalance)
	{
		LOG(ERR, __FUNCTION__, " pBalance is NULL");
		return ACK_UNKNOWN_ERROR;
	}

	// 길드 탈퇴 점수
	int leaveScore = 0;
	{
		auto pFly = pBalance->GetFly(GUILD_LEAVE_POINT);
		if (NULL == pFly)
		{
			LOG(ERR, __FUNCTION__, " pFly(GUILD_LEAVE_POINT) is NULL");
			return ACK_UNKNOWN_ERROR;
		}

		leaveScore = pFly->_VALUE;
	}

	int guildScore = m_GuildInfo._score + leaveScore;
	if ( 0 > guildScore )
		guildScore = 0; // added by spencer(2014.08.13), 음수가 나와서 아래에 guildGrade가 -1이 되는 문제가 발생함.
	int guildGrade = pBalance->GetGuildGradeByScore(guildScore);

	if (0 >= guildGrade)
	{
		LOG(ERR, __FUNCTION__, " Guild grade get fail. score : ", guildScore);
		return ACK_UNKNOWN_ERROR;
	}

	Database::ResultSet res = Database::Execute(static_cast<int>(DatabaseType::GUILD_MASTER),
				"call sp_update_kickout_guild_member(",
					m_GuildSeq, ",",
					member_seq, ",",
					guildScore, ",",
					guildGrade, ")"
			);

	if (0 != res.GetSQLError())
	{
		LOG(ERR, __FUNCTION__, " Fail call sp_update_kickout_guild_member");
		return ACK_UNKNOWN_ERROR;
	}

	auto iter = res.begin();

	int nRet = iter->getInt(1);

	if (0 != nRet)
	{
		LOG(ERR, __FUNCTION__, " Guild member kickout exception. nRet : ", nRet);
		return nRet;
	}

	ChangeScore(leaveScore);

	// 등급이 낮아 지는 경우 Buff Down
	if (m_GuildInfo._grade > guildGrade)
		UpdateBuffDown();

	m_GuildInfo._grade = guildGrade;

	// 길드에서 멤버 삭제
	DelMember(member_seq);

	return ACK_OK;
}

void
Guild::UpdateGradeDown()
{
	/*
	 * 길드 점수에 의해 길드 등급이 낮아지는 경우에 DB 갱신
	 */

	NResource::ResourceGuildBanalce * pBalance = NResource::Static::Instance().GetGuildBalance();
	if (NULL == pBalance)
	{
		LOG(ERR, __FUNCTION__, " pBalance is NULL");
		return;
	}

	int guildGrade = pBalance->GetGuildGradeByScore(m_GuildInfo._score);
	if (0 >= guildGrade)
	{
		LOG(ERR, __FUNCTION__, " Guild grade get fail. score : ", m_GuildInfo._score);
		return;
	}

	// 등급 변동이 없거나, 높아지는 경우는 SKIP.
	if (guildGrade >= m_GuildInfo._grade)
		return;

	Database::ResultSet res = Database::Execute(static_cast<int>(DatabaseType::GUILD_MASTER),
				"call sp_update_guild_grade(", m_GuildSeq, ",", guildGrade, ")"	);

	if (0 != res.GetSQLError())
	{
		LOG(ERR, __FUNCTION__, " Fail call sp_update_guild_grade.");
		return;
	}

	auto iter = res.begin();

	int nRet = iter->getInt(1);

	if (0 != nRet)
	{
		LOG(ERR, __FUNCTION__, " call sp_update_guild_grade. nRet : ", nRet);
		return;
	}

	m_GuildInfo._grade	= guildGrade;

	UpdateBuffDown();
}


void
Guild::UpdateBuffDown()
{
	if (0 >= m_GuildInfo._buffType)
		return;

	// 길드등급이 낮아 지는 경우 Buff Value도 하락
	NResource::ResourceGuildBuffValue * pBuffValue = NResource::Static::Instance().GetGuildBuffValue();
	if (NULL == pBuffValue)
	{
		LOG(ERR, __FUNCTION__, " pBuffValue is NULL.");
		return;
	}

	m_GuildInfo._buffLevel -= 1;
	if (0 >= m_GuildInfo._buffLevel)
		m_GuildInfo._buffLevel = 1;

	NResource::ResourceGuildBuffValue::Key clsKey;
	clsKey.tid		= m_GuildInfo._buffType;
	clsKey.level	= m_GuildInfo._buffLevel;

	NResource::ResourceGuildBuffValue::Flyweight * pFly = pBuffValue->GetFly(clsKey);
	if (NULL == pFly)
	{
		LOG(ERR, __FUNCTION__, " pFly is NULL. buff_tid : ", m_GuildInfo._buffType, " buff_leve : ", m_GuildInfo._buffLevel);
		return;
	}

	m_GuildInfo._buffValue = pFly->skill_value;

	// 버프 정보 DB 갱신
	Database::ResultSet res = Database::Execute(static_cast<int>(DatabaseType::GUILD_MASTER),
				"call sp_update_guild_buff(",
					m_GuildSeq, ",",
					m_GuildInfo._buffType, ",",
					m_GuildInfo._buffLevel, ",",
					m_GuildInfo._buffValue, ")"
			);

	if (0 != res.GetSQLError())
	{
		LOG(ERR, __FUNCTION__, " Fail call sp_update_guild_buff.");
		return;
	}

	auto iter = res.begin();

	int nRet = iter->getInt(1);

	if (0 != nRet)
	{
		LOG(ERR, __FUNCTION__, " Guild Buff update exception. nRet : ", nRet);
		return;
	}
}

void
Guild::OnTime()
{
	LOG(DEV, __FUNCTION__, " : Booooooom Timer. guild_seq : ", m_GuildSeq);

	FlushGuildPoint();

	auto iter = m_MemberTree.begin();
	for (; iter != m_MemberTree.end(); ++iter)
	{
		iter->second->FlushMemberPoint();
	}

	m_bWorkTimer = false;
}

void
Guild::SetTimer()
{
	// Timer가 이미 가동중
	if (true == m_bWorkTimer)
		return;

	m_bWorkTimer = true;
	m_pTimer->SetTimer(1000 * 60 * 10, std::bind(&Guild::OnTime, this));
}

} /*GuildServer*/


